package edu.buaa.act.model.tsc;

import edu.buaa.act.helper.Utils;
import edu.buaa.act.model.GPSPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by T7810 on 2017/4/22.
 */
public class AnonymousSampler
{
    private Logger log = Utils.getDebugLogger();

    private final long sampleStartTimestamp;
    private final long sampleEndTimestamp;
    private List<List<GPSPoint>> allData;
    private List<List<GPSPoint>> trainingData = new ArrayList<>();
    private List<List<GPSPoint>> testData = new ArrayList<>();
    private List<Integer> carIDList = new ArrayList<>();
    private int sampleCount;
    private SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // sampleStartTimestamp is a millisecond timestamp,
    // before which is training time interval, after which is test time interval.
    public AnonymousSampler(List<List<GPSPoint>> allRawData, int sampleCount, String sampleStartTime, String sampleEndTime) throws ParseException
    {
        if(sampleCount>=allRawData.size())
        {
            log.error("total car count {}, but try to sample {} cars!", allRawData.size(), sampleCount);
            throw new RuntimeException("sample more than total car count!");
        }
        this.df.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
        this.allData = allRawData;
        this.sampleCount = sampleCount;
        this.sampleStartTimestamp = df.parse(sampleStartTime).getTime();
        this.sampleEndTimestamp = df.parse(sampleEndTime).getTime();
        Random random = new Random(System.currentTimeMillis());
        Set<Integer> sampleIndexSet = new HashSet<>();
        while(sampleIndexSet.size()<sampleCount){
//            int index = random.nextInt(sampleCount);
            int index = random.nextInt(allRawData.size());
            sampleIndexSet.add(index);
        }
        for(int i=0; i<allRawData.size(); i++)
        {
            if(sampleIndexSet.contains(i)){
                testData.add(allRawData.get(i));
                carIDList.add(allRawData.get(i).get(0).getCarID());
            }else{
                trainingData.add(allRawData.get(i));
            }
        }
        log.debug("test {} ({}), train {}", testData.size(), getTestCarIDStr(' '), trainingData.size());
    }

    public List<List<GPSPoint>> getTrainingData()
    {
        return trainingData;
    }



    public List<List<GPSPoint>> getTestData()
    {

        log.debug("return test trajectories only after {} (exclusive)", df.format(new Date(sampleStartTimestamp)));

        List<List<GPSPoint>> result = new ArrayList<>();
        for(List<GPSPoint> traj : testData)
        {
            List<GPSPoint> cutTraj = new ArrayList<>();
            for(GPSPoint point : traj)
            {
                if(sampleStartTimestamp < point.getTimestamp() && point.getTimestamp() < sampleEndTimestamp )
                {
                    cutTraj.add(point);
                }
            }
            result.add(cutTraj);
        }
        return result;
    }

    public int getSampleCount()
    {
        return sampleCount;
    }

    public void saveSampleToFile(File output) throws IOException
    {
        BufferedWriter w = new BufferedWriter(new FileWriter(output));
        for(List<GPSPoint> traj: testData)
        {
            for (GPSPoint p : traj) {
                w.write(p.toStringShort('\t'));
                w.write('\n');
            }
        }
        w.close();
    }


    public List<Integer> getTestCarIDList()
    {
        return carIDList;
    }

    public String getTestCarIDStr(char sep)
    {
        if( carIDList.isEmpty() ) return "[no test car data]";
        Collections.sort(carIDList);
        StringBuilder s = new StringBuilder();
        for(Integer id : carIDList)
        {
            s.append(id).append(sep);
        }
        return s.substring(0, s.length()-1);
    }

    public long getSampleStartTimestamp()
    {
        return sampleStartTimestamp;
    }

    public long getSampleEndTimestamp()
    {
        return sampleEndTimestamp;
    }
}
